<template>
	<view class="content" style="background-color: #F4F4F4;height: 100vh;">
		<tn-nav-bar bgClass="bg-gradual-blue" isBack>
			<view>
				{{ bleName }}
			</view>
		</tn-nav-bar>
		<view :style="{paddingTop: vuex_custom_bar_height + 'px'}" class="bg-white">
			<tn-sticky enabled :customNavHeight='vuex_custom_bar_height' class="bg-white">
				<view class="bg-white tn-margin">
					<tn-subsection class="bg-white tn-margin-top-sm " :list="topList"
						@change='tabChange'></tn-subsection>
				</view>
			</tn-sticky>

			<view v-if="topKey!=='update'">
				<view class="margin-sm">
					<view class="margin-tb-sm" v-for="key in Object.keys(configData[topKey])" :key="key">
						<tn-list-cell hover radius @click="showKeyboard(configData[topKey][key])">
							<view class="list-icon-text">
								<view class="list__left">
									<view class="list__left__text">{{configData[topKey][key].text}}</view>
								</view>
								<view class="list__right">
									<text
										class="text-lg text-bold">{{configData[topKey][key].tValue?configData[topKey][key].tValue:0}}</text>
									<text class=" tn-flex-1 padding-left-sm"> {{configData[topKey][key].unit}}</text>
								</view>
							</view>
						</tn-list-cell>
					</view>
				</view>
			</view>



			<!-- 升级页面代码 -->
			<view v-if="topKey==='update'" style="width: 100%;">
				<view class="tn-padding-sm">
					<tn-radio-group class="tn-margin" width="100%" :value="selectedMode" wrap>
						<tn-radio v-for="item in radioItems" :name="item.value+''" :key="item.value">
							<view class="tn-padding-sm">
								{{item.name}}
							</view>
						</tn-radio>
					</tn-radio-group>

					<button class="tn-margin tn-bg-blue" @tap="chooseFile" :disabled="upgrading">选择固件文件</button>
					<text class="tn-margin tn-text-bold tn-text-xl">{{ filename }}</text>

					<button class="tn-margin tn-bg-green" @tap="startUpgrade" :disabled="upgrading">开始升级</button>
					<progress :percent="progress" show-info />
					<textarea class="tn-margin" style="height: 200px;" :value="log" :maxlength='-1' disabled />
				</view>
			</view>

			<view style="height: 10vh;"></view>


			<tn-modal :value="showKey" @click="setBleVal" @cancel="onDialogClose" custom :button="buttons"
				padding='30rpx'>
				<view class="custom-modal-content">
					<tn-form :labelWidth="140" style="width: 100%;">
						<tn-form-item :borderBottom="false" :label="activeItem.text" labelPosition="top">
							<tn-input v-model="inputValue" border type="number"></tn-input>
							<text slot="right" class="tn-text-bold">{{activeItem.unit}}</text>
						</tn-form-item>
					</tn-form>
				</view>
			</tn-modal>
		</view>
	</view>

</template>

<script>
	import BLE from "@/libs/bluetooth/BLE.js";
	import Protocol from "@/libs/protocol/tianyi/protocol.js";
	import bleData from "@/libs/protocol/tianyi/bleData.js";
	import CRC16 from "@/libs/protocol/tianyi/CRC16";

	let timer = null;
	let sendTimer = null;



	export default {
		data() {
			return {
				isBreak: false,
				bleName: '',
				topKey: 'B_CONFIG',
				topList: _topList,
				inputValue: '',
				showKey: false,
				canSendCMD: true,
				isFirst: true,
				isWrite: false,
				readIndex: 0,
				activeItem: null,
				filename: '',
				fileData: null,
				progress: 0,
				log: '',
				upgrading: false,
				radioItems: _radioItems,
				selectedMode: '1',
				configData: {
					B_CONFIG: bleData.B_CONFIG,
					C_CONFIG: bleData.C_CONFIG,
					Bal_Sleep: bleData.Bal_Sleep,
					Cali_CONFIG: bleData.Cali_CONFIG,
				},
				buttons: [{
						text: '取消',
						backgroundColor: '#AAAAAA',
						fontColor: '#FFFFFF'
					},
					{
						text: '确定',
						backgroundColor: '#01BEFF',
						fontColor: '#FFFFFF'
					}
				],
			}
		},
		onLoad(options) {
			// 保持屏幕常亮
			uni.setKeepScreenOn({
				keepScreenOn: true
			});
			if (options.n) {
				this.bleName = options.n
			}
		},
		onShow() {
			var that = this;
			if (timer != null) {
				clearInterval(timer)
				timer = null;
			}
			let readValue = {}
			//设置值更改函数
			that.SetValueChanged();
			that.isWrite = false;
			that.readIndex = 0;
			
			//请求版本号
			Protocol.writeStrToRead('0103A40000106736', function(res) {
				console.log('请求版本号');
			});
			
			
			that.sendFirstToBle()
			//定时读取蓝牙数据
			timer = setInterval(() => {
				that.sendFirstToBle()
			}, 2000)
		},
		methods: {
			toHomePage(){
				uni.reLaunch({
					url: '/pages_ble/ble/tianyi'
				})
			},
			setBleVal(e) {
				if (e.index < 1) {
					this.showKey = false
					this.inputValue = ''
					return
				}
				let that = this
				this.$tn.message.toast('下发中')
				let address = that.activeItem.address
				let v = parseFloat(that.inputValue);
				if (!isNaN(v)) {
					Protocol.writeToWrite(address, Protocol.encryption(address, v), async () => {
						console.log('发送指令成功');
						that.showKey = false
						that.$tn.message.toast('指令下发成功')
						setTimeout(()=>{
							that.canSendCMD = true;
						},500)
					})
					setTimeout(()=>{
						if(that.showKey){
							that.canSendCMD = true;
							that.showKey = false
							that.$tn.message.toast('指令下发失败')
						}
					},2000)
				}else{
					that.canSendCMD = true;
				}
			},
			onDialogClose() {
				this.showKey = false;
			},
			showKeyboard(item) {
				this.canSendCMD = false
				this.activeItem = item;
				this.showKey = true
				this.inputValue = item.tValue
			},
			tabChange(e) {
				console.log(e);
				this.topKey = e.obj.key
			},
			chooseFile() {
				// #ifdef MP-WEIXIN
				uni.chooseMessageFile({
					count: 1,
					type: 'file',
					success: res => {
						const file = res.tempFiles[0];
						this.filename = file.name;
						const fsm = uni.getFileSystemManager();
						try {
							this.fileData = fsm.readFileSync(file.path);
							console.log('文件字节:', this.fileData.byteLength);
							this.appendLog('文件读取成功');
						} catch (e) {
							this.appendLog('文件读取失败')
							console.error('读取失败:', e);
						}

					}
				});
				// #endif
			},
			radioChange(e) {
				this.selectedMode = e.detail.value;
				this.radioItems.forEach(item => {
					item.checked = (item.value === this.selectedMode);
				});
			},
			sendFirstToBle() {
				let that = this
				var state = getApp().globalData.bleObj.state;
				var deviceId = getApp().globalData.bleObj.deviceId;
				var isWrite = that.isWrite;
				if (state && deviceId != '' && isWrite === false && this.topKey !== 'update') {
					that.readIndex = 0;
					that.isWrite = true;
					Protocol.writeStrToRead(CMDs[0].cmd, function(res) {
						console.log('sendFirstToBle');
					});
				}
			},
			//设置蓝牙请求数据
			SetValueChanged() {
				console.log('SetValueChanged');

				var that = this;
				// 设置蓝牙连接状态变化回调
				BLE.bleCallBack.ConnectionStateChange = function(res) {
					var connected = res.connected;
					if (!connected) {
						console.log('蓝牙断开连接');
						getApp().globalData.bleObj.state = false
						BLE.Connect(getApp().globalData.bleObj.deviceId,()=>{})
					} else {
						console.log('蓝牙已连接');
					}
				}
				//处理数据
				BLE.bleCallBack.CharacteristicValueChange = function(res) {
					if (res != undefined && res != null && res != '') {
						// console.log('蓝牙返回数据', res);
						if (that.topKey === 'update' && res.toUpperCase() === '0190030C01') {
							that.isBreak = true
						}
						if (that.topKey !== 'update'&&that.canSendCMD) {
							var current = CMDs[that.readIndex];
							var packages = Protocol.solutionData(res);
							if (packages) {
								that.SetTargetVal(that.configData[current.name], packages.data)
								that.readIndex++;
								if (that.readIndex > 3) {
									that.readIndex = 0;
								}
								Protocol.writeStrToRead(CMDs[that.readIndex].cmd, function(res) {
									//console.log('发送指令',CMDs[that.readIndex].cmd);
								});

							} else {
								that.isWrite = false
							}
						}else{
							that.isWrite = false
						}
					}
				}
				//处理数据
				BLE.bleCallBack.notifyBLE = function(res) {
					if (res != undefined && res != null && res != '') {
						console.log('蓝牙返回数据', res);
					}
				}
			},

			async startUpgrade() {
				console.log('startUpgrade');
				if (!this.fileData) {
					this.appendLog('请先选择固件文件');
					return;
				}
				this.upgrading = true;
				this.progress = 0;
				this.log = '';
				// 1. 初始化蓝牙并连接设备
				const bleOk = getApp().globalData.bleObj.state;
				if (!bleOk) {
					this.appendLog('蓝牙初始化或连接失败');
					this.upgrading = false;
					return;
				}
				const pageSize = 128;
				const total = Math.ceil(this.fileData.byteLength / pageSize);
				console.log('this.fileData.byteLength ：', this.fileData.byteLength);
				console.log('文件大小：', total);
				// 2. 设备握手
				const handshakeOk = await this.deviceHandshake(total);
				if (!handshakeOk) {
					this.appendLog('握手失败');
					this.upgrading = false;
					return;
				}
				this.appendLog('握手成功，开始发送数据...');
				// 3. 分包发送

				for (let i = 0; i < total; i++) {
					if (this.isBreak) {
						break;
					}
					const chunk = this.fileData.slice(i * pageSize, (i + 1) * pageSize);
					const ok = await this.sendChunk(chunk, i);
					if (!ok) {
						this.appendLog(`第${i + 1}包发送失败`);
						this.upgrading = false;
						return;
					}
					this.progress = Math.floor(((i + 1) / total) * 100);
					this.appendLog(`第${i + 1}包发送成功`);
				}
				if (!this.isBreak) {
					// 4. 发送升级完成指令
					await this.sendFinish();
					this.appendLog('升级完成');
					this.upgrading = false;
				} else {
					this.appendLog('升级失败');
				}

			},
			appendLog(msg) {
				this.log = msg + '\n' + this.log;
			},
			// 2. 设备握手
			async deviceHandshake(size) {
				console.log('0110F0000001020001979F');
				// 发送握手指令（如0x00,0x01），并等待设备响应
				const handshakeCmd = '0110F0000001' + size.toString(16).toUpperCase().padStart(4, '0');
				let cmdStr = handshakeCmd + CRC16.ToModbusCRC16(handshakeCmd)
				console.log('deviceHandshake', cmdStr);
				return await this.writeBLE('0110F0000001020001979F');
			},
			// 3. 分包发送
			async sendChunk(chunk, index) {
				let address = 61441 + index
				let addressHex = (address).toString(16).toUpperCase().padStart(4, '0')
				let size = '80'
				if (chunk.byteLength < 128) {
					console.log('chunk.byteLength != 128', chunk.byteLength);
					chunk = fullBuffer(chunk)
					console.log('chunk.byteLength',chunk.byteLength);
					size = (chunk.byteLength).toString(16).toUpperCase().padStart(2, '0')
				}
				let sendCmd = '0110' + addressHex + "0040" + size + arrayBufferToHex(chunk)
				console.log('sendChunk', sendCmd);
				let cmdStr = sendCmd + CRC16.ToModbusCRC16(sendCmd)
				return await this.writeBLE(cmdStr);
			},
			// 4. 发送升级完成指令
			async sendFinish() {
				// 发送升级完成指令，内容根据协议
				const finishCmd = '0110FEAA000102' + convertToHex(Number(this.selectedMode));
				let cmdStr = finishCmd + CRC16.ToModbusCRC16(finishCmd)
				console.log('sendFinish', cmdStr);
				return await this.writeBLE(cmdStr);
			},
			// BLE写入封装
			async writeBLE(data) {
				// 如果 data 是 Uint8Array 或 ArrayBuffer，直接使用
				let command;
				if (data instanceof ArrayBuffer) {
					command = new Uint8Array(data);
				} else if (data instanceof Uint8Array) {
					command = data;
				} else {
					// 如果是十六进制字符串
					const matches = data.match(/[\da-f]{2}/gi);
					if (!matches) return Promise.resolve(false);
					command = new Uint8Array(matches.map(h => parseInt(h, 16)));
				}
				let deviceId = getApp().globalData.bleObj.deviceId;
				let serviceId = getApp().globalData.bleObj.serviceId;
				let characteristicId = getApp().globalData.bleObj.characteristicId;
				return new Promise((resolve) => {
					uni.writeBLECharacteristicValue({
						deviceId: deviceId,
						serviceId: serviceId,
						characteristicId: characteristicId,
						value: command.buffer ? command.buffer : command,
						success: () => {
							setTimeout(() => {
								resolve(true)
							}, 300)
						},
						fail: () => resolve(false)
					});
				});
			},
			SetTargetVal(targetObj, valStr) {
				let targetKeys = Object.keys(targetObj)
				for (var i = 0; i < targetKeys.length; i++) {
					let key = targetKeys[i]
					let target = targetObj[key]
					target.value = valStr.substr(i * 4, 4);
					let num = Protocol.algorithm(target.address,target.value)
					const n = parseFloat(num);
					const tValue = isNaN(n) ? num : n % 1 === 0 ? n : parseFloat(n.toFixed(1));
					target.tValue = tValue;
					targetObj[key] = target
				}
			},
		}
	}

	function convertToHex(input, n) {
		// 步骤 1: 将输入字符串转为整数
		const number = parseInt(input, 10);

		// 步骤 2: 转为十六进制字符串，并转为大写
		let hex = number.toString(16).toUpperCase();

		// 步骤 3: 补零到 4 位
		while (hex.length < (n ? n : 4)) {
			hex = '0' + hex;
		}
		return hex;
	}

	function fullBuffer(originalBuffer){
		if (originalBuffer.byteLength >= 128) {
		    return originalBuffer; // 如果已经满足长度，直接返回
		}
		let newBuffer = new ArrayBuffer(128);
		let view = new Uint8Array(newBuffer);
		view.set(new Uint8Array(originalBuffer)); // 复制旧数据
		// 剩余字节自动填充为 0x00
		return newBuffer; // 返回新的 ArrayBuffer
	}

	function arrayBufferToHex(buffer) {
		if (buffer instanceof Uint8Array) {
			return Array.from(buffer)
		}
		const arr = new Uint8Array(buffer);
		return Array.from(arr)
			.map(b => b.toString(16).padStart(2, '0'))
			.join('');
	}


	const _radioItems = [{
			name: '仅升级',
			value: '1',
			checked: true
		},
		{
			name: '擦除保护参数和基础配置后升级',
			value: '2',
			checked: false
		},
		{
			name: '为擦除全部区域后进行升级',
			value: '3',
			checked: false
		},
		{
			name: '模式4',
			value: '4',
			checked: false
		}
	];

	const _topList = [{
			name: '基础参数',
			key: 'B_CONFIG'
		},
		{
			name: '保护配置',
			key: 'C_CONFIG'
		},
		{
			name: '均衡休眠',
			key: 'Bal_Sleep'
		},
		{
			name: '校准参数',
			key: 'Cali_CONFIG'
		},
		{
			name: '升级',
			key: 'update'
		}
	]



	const CMDs = [{
		name: 'B_CONFIG', //基础参数
		cmd: '0103A50000146709'
	}, {
		name: 'C_CONFIG',
		cmd: '0103A6000036E754'
	}, {
		name: 'Bal_Sleep',
		cmd: '0103A636001A0687'
	}, {
		name: 'Cali_CONFIG',
		cmd: '0103A700001F2776'
	}]
</script>

<style lang="scss" scoped>
	progress {
		margin: 20rpx 0;
	}

	textarea {
		width: 100%;
		height: 200rpx;
		margin-top: 20rpx;
	}


	.list {
		&__left {
			display: flex;
			align-items: center;
			justify-content: flex-start;

			&__icon,
			&__image {
				margin-right: 18rpx;
			}
		}

		&__right {
			display: flex;
			align-items: center;
			justify-content: flex-end;
		}
	}

	.list-icon-text,
	.list-image-text {
		display: flex;
		align-items: center;
		justify-content: space-between;
	}

	.list-image-text {
		.list {
			&__left {
				&__image {
					width: 20rpx;
					height: 20rpx;
				}
			}
		}
	}
</style>